package org.volicy.mysql.schema.util;

import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.MySQLIndexHint;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.drop.Drop;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.FromItem;
import net.sf.jsqlparser.statement.select.Join;
import net.sf.jsqlparser.statement.select.Pivot;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.update.Update;
import net.sf.jsqlparser.util.deparser.ExpressionDeParser;
import net.sf.jsqlparser.util.deparser.SelectDeParser;
import net.sf.jsqlparser.util.deparser.StatementDeParser;
import org.springframework.util.StringUtils;
import sun.security.krb5.internal.crypto.Aes128;

import java.util.List;

/**
 * @Author: volicy.xu
 * @Date: 2021/9/15
 */
public class SqlParserUtil {

    /**
     * @param buffer
     * @param insert
     * @param schemaName
     * @return
     */
    private static String sqlParserOfInsert(StringBuilder buffer,
                                            Insert insert,
                                            String schemaName) {
        Table table = insert.getTable();
        table.setSchemaName(getSchemaName(table, schemaName));
        insert.setTable(table);
        return sql(buffer, insert, schemaName);
    }

    /**
     * @param buffer
     * @param update
     * @param schemaName
     * @return
     */
    private static String sqlParserOfUpdate(StringBuilder buffer,
                                            Update update,
                                            String schemaName) {
        Table table0 = update.getTable();
        if (table0 != null ) {
            table0.setSchemaName(getSchemaName(table0, schemaName));
            update.setTable(table0);
        }
        FromItem fromItem = update.getFromItem();
        if (fromItem != null) {
            Table table = (Table) fromItem;
            table.setSchemaName(getSchemaName(table, schemaName));
        }
        List<Join> joins = update.getJoins();
        if (joins != null) {
            joins.forEach(j -> {
                Table table = (Table) j.getRightItem();
                table.setSchemaName(getSchemaName(table, schemaName));
            });
        }
        return sql(buffer, update, schemaName);

    }


    /**
     * @param buffer
     * @param delete
     * @param schemaName
     * @return
     */
    private static String sqlParserOfDelete(StringBuilder buffer,
                                            Delete delete,
                                            String schemaName) {
        List<Table> tables = delete.getTables();

        if (tables != null) {
            tables.forEach(tab -> {
                tab.setSchemaName(getSchemaName(tab, schemaName));
            });
            delete.setTables(tables);
        }
        Table table1 = delete.getTable();
        table1.setSchemaName(getSchemaName(table1, schemaName));
        List<Join> joins = delete.getJoins();
        if (joins != null) {
            joins.forEach(j -> {
                Table table = (Table) j.getRightItem();
                table.setSchemaName(getSchemaName(table, schemaName));
            });
        }
        return sql(buffer, delete, schemaName);
    }

    /**
     * @param buffer
     * @param drop
     * @param schemaName
     * @return
     */
    private static String sqlParserOfDrop(StringBuilder buffer,
                                          Drop drop,
                                          String schemaName) {
        Table table = drop.getName();
        table.setSchemaName(getSchemaName(table, schemaName));
        drop.setName(table);
        return sql(buffer, drop, schemaName);
    }

    /**
     * @param sql
     * @param schemaName
     * @return
     * @throws JSQLParserException
     */
    public static String sqlParser(String sql, String schemaName) throws JSQLParserException {
        StringBuilder buffer = new StringBuilder();
        Statement statement = CCJSqlParserUtil.parse(sql);
        if (statement instanceof Insert) {
            return sqlParserOfInsert(buffer, (Insert) statement, schemaName);
        } else if (statement instanceof Update) {
            return sqlParserOfUpdate(buffer, (Update) statement, schemaName);
        } else if (statement instanceof Delete) {
            return sqlParserOfDelete(buffer, (Delete) statement, schemaName);
        } else if (statement instanceof Drop) {
            return sqlParserOfDrop(buffer, (Drop) statement, schemaName);
        }
        return sql(buffer, statement, schemaName);
    }

    /**
     * @param tab
     * @param schemaName
     * @return
     */
    private static String getSchemaName(Table tab, String schemaName) {
        return StringUtils.hasText(tab.getSchemaName()) ? tab.getSchemaName() : schemaName;
    }

    /**
     * @param buffer
     * @param statement
     * @param schemaName
     * @return
     */
    private static String sql(StringBuilder buffer, Statement statement, String schemaName) {
        statement.accept(new StatementDeParser(
                new ExpressionDeParser(),
                new SqlParserUtil.HMSelectDeParser(schemaName), buffer));
        return buffer.toString();
    }

    static class HMSelectDeParser extends SelectDeParser {
        private String schemaName;


        public HMSelectDeParser(String schemaName) {
            super();
            this.schemaName = schemaName;
        }

        /**
         * @param tableName
         */
        public void visit(Table tableName) {
            tableName.setSchemaName(getSchemaName(tableName, this.schemaName));
            this.buffer.append(tableName.getFullyQualifiedName());
            Alias alias = tableName.getAlias();
            if (alias != null) {
                this.buffer.append(alias);
            }

            Pivot pivot = tableName.getPivot();
            if (pivot != null) {
                pivot.accept(this);
            }
            MySQLIndexHint indexHint = tableName.getIndexHint();
            if (indexHint != null) {
                this.buffer.append(indexHint);
            }
        }
    }
}
